#+TITLE:   Cofiguración de Emacs usando Doom Emacs
#+DATE:    mayo 13, 2019
#+SINCE:   {replace with next tagged release version}

* Config
** init.el
#+BEGIN_SRC emacs-lisp :tangle init.el
(doom! :completion
       (company +auto
                +childframe)
       (ivy +childframe
            +prescient)

       :ui
       doom-dashboard
       doom-quit
       hl-todo
       indent-guides
       modeline
       nav-flash
       neotree
       ophints
       (popup +all
              +defaults)
       (pretty-code +pragmata-pro)
       unicode
       vc-gutter
       window-select
       workspaces

       :editor
       file-templates
       (format +onsave)
       multiple-cursors
       parinfer
       rotate-text
       snippets

       :emacs
       dired
       electric
       vc

       :term
       term

       :tools
       ansible
       docker
       editorconfig
       eval
       flycheck
       (flyspell +hunspell)
       (lookup +docsets)
       lsp
       magit
       make
       (pass +auth)
       pdf
       rgb
       tmux
       upload

       :lang
       data
       emacs-lisp
       (javascript +lsp)
       latex
       (org +attach
            +babel
            +capture
            +export
            +habit
            +protocol
            +rest)
       (python +lsp)
       rest
       (sh +zsh)

       :app
       (write +langtool)

       :collab

       :config
       (default +bindings +smartparens))
#+END_SRC
** Auto Tangle
#+BEGIN_SRC emacs-lisp :tangle config.el
(defun tangle-init-async ()
  "If the current buffer is 'README.org' the code-blocks are tangled."
  (when (or (equal (buffer-file-name) (expand-file-name (concat doom-private-dir "README.org")))
            (equal (buffer-file-name) (expand-file-name "~/.dots/doom/README.org")))
    (let ((prog-mode-hook nil))
      (async-shell-command "sh -c \"~/.emacs.d/bin/org-tangle ~/.doom.d/README.org\"" "*Messages*"))))

(add-hook 'after-save-hook 'tangle-init-async)
#+END_SRC
** Lang
#+BEGIN_SRC emacs-lisp :tangle config.el
(setq current-language-environment "Spanish"
      org-export-default-language "es")
#+END_SRC
** Doom
#+BEGIN_SRC emacs-lisp :tangle config.el
(setq doom-projectile-fd-binary "fd"
      confirm-kill-emacs nil)
#+END_SRC
* Editor
** Company
#+BEGIN_SRC emacs-lisp :tangle config.el
 (after! company
   (setq company-idle-delay 0
         company-show-numbers t
         company-minimum-prefix-length 1
         company-tooltip-limit 14
         company-dabbrev-downcase nil
         company-dabbrev-ignore-case nil
         company-dabbrev-code-other-buffers t
         company-tooltip-align-annotations t
         company-require-match 'never
         company-global-modes '(not erc-mode message-mode help-mode gud-mode eshell-mode)
         company-backends '(company-capf)
         company-frontends '(company-pseudo-tooltip-frontend
                             company-echo-metadata-frontend)))
#+END_SRC
** Highlight
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! hl-line :disable t)
#+END_SRC
** Move-dup
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! move-dup)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! move-dup
  :defer t
  :diminish move-dup-mode
  :bind (("S-M-<up>" . md/move-lines-up)
         ("S-M-<down>" . md/move-lines-down)
         ("C-M-<up>" . md/duplicate-up)
         ("C-M-<down>" . md/duplicate-down))
  :init (global-move-dup-mode))
#+END_SRC
** Snnipets
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! org-sync-snippets)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! org-sync-snippets
  :init (add-hook 'yas-after-reload-hook 'org-sync-snippets-org-to-snippets)
  :config (setq org-sync-snippets-snippets-dir +snippets-dir
                org-sync-snippets-org-snippets-file (concat +snippets-dir "snippets.org")))
#+END_SRC
* UI
** Cursor
#+BEGIN_SRC emacs-lisp :tangle config.el
(setq-default cursor-type '(hbar . 2))
(setq x-stretch-cursor t)

(use-package! frame
  :config
  (defun set-cursor-hook (frame)
    (modify-frame-parameters
     frame (list (cons 'cursor-color "white"))))
  (add-hook 'after-make-frame-functions 'set-cursor-hook))

(add-to-list 'default-frame-alist
             '(ns-transparent-titlebar . t))
(add-to-list 'default-frame-alist
             '(ns-appearance . dark))
#+END_SRC
** Fringe
#+BEGIN_SRC emacs-lisp :tangle config.el
(fringe-mode nil)
#+END_SRC
** Font
#+BEGIN_SRC emacs-lisp :tangle config.el
(setq doom-font (font-spec :family "PragmataPro Mono Liga" :pixelsize 13.5))
#+END_SRC
** Imenu
*** Package
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! imenu-list)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! imenu-list
  :init
  (set-popup-rule! "^\\*Ilist"
    :slot -1 :vslot -1 :size 35 :side 'left :ttl 0)
  (setq imenu-list-focus-after-activation t))

(map! :leader "o i" #'imenu-list-smart-toggle)
#+END_SRC
** Ivy
#+BEGIN_SRC emacs-lisp :tangle config.el
(after! ivy-posframe
  (setq ivy-posframe-hide-minibuffer nil
        ivy-fixed-height-minibuffer nil
        ivy-posframe-parameters `((min-width . ,(window-width))
                                  (min-height . ,ivy-height)
                                  (internal-border-width . 0))
        ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-window-bottom-left))))

(after! counsel
  (setq counsel-find-file-at-point t
        counsel-rg-base-command "rg -uuu -S -g '!/volumes' -g '!/backups' -g '!/.git' --no-heading --line-number --color never %s ."))
#+END_SRC
** Line Numbers
#+BEGIN_SRC emacs-lisp :tangle config.el
(setq display-line-numbers-type nil)
#+END_SRC
** Maximized
#+BEGIN_SRC emacs-lisp :tangle config.el
(set-frame-parameter nil 'fullscreen 'maximized)
#+END_SRC
** Modeline
#+BEGIN_SRC emacs-lisp :tangle config.el
(after! doom-modeline
  (defun conf:doom-modeline--make-xpm-filter-args (args)
    "Force function to use `doom-modeline-height'.
     Instead of the calculation done in `doom-modeline-refresh-bars'.
     The minimum height is set to `frame-char-height' + 2."
    (list (car args) (cadr args) (max (+ (frame-char-height) 2) doom-modeline-height)))

  (advice-add 'doom-modeline--make-xpm :filter-args #'conf:doom-modeline--make-xpm-filter-args)

  (setq doom-modeline-icon nil
        doom-modeline-height 12
        doom-modeline-env-enable-python nil))
#+END_SRC
** Neotree
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! shrink-path)

(after! neotree
  (setq neo-theme 'ascii
        neo-vc-integration nil
        neo-window-width 36
        neo-create-file-auto-open t
        neo-smart-open t
        neo-show-auto-change-root t
        neo-autorefresh nil
        neo-banner-message nil
        neo-mode-line-type 'neotree
        neo-dont-be-alone t
        neo-persist-show t
        neo-show-updir-line nil
        neo-show-hidden-files nil
        neo-auto-indent-point t
        neo-hidden-regexp-list '(".DS_Store" ".idea/" ".pyc" ".tern-port"
                                 ".git/*" "node_modules/*" ".meteor" "_build" "deps"))
  (defun shrink-root-entry (node)
    "shrink-print pwd in neotree"
    (insert (propertize (concat (shrink-path-dirs node) "\n") 'face `(:inherit (,neo-root-dir-face)))))

 (advice-add #'neo-buffer--insert-root-entry :override #'shrink-root-entry))
#+END_SRC
** Pretty Code
#+BEGIN_SRC emacs-lisp :tangle config.el :tangle no
;; (defvar +pretty-code-extra-ligatures
;;   '(("==" . ?\u2261)
;;     ("!=" . ?\u2260)
;;     (">=" . ?\u2265)
;;     ("<=" . ?\u2264)))

(setq-default prettify-symbols-alist
              (append prettify-symbols-alist
                      +pretty-code-extra-ligatures))
#+END_SRC
** Theme
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! vibrant-ink-theme
  :recipe (:host github :repo "arkhan/vibrant-ink-theme"))
#+END_SRC
#+BEGIN_SRC emacs-lisp :tangle config.el
(setq doom-theme 'vibrant-ink)
#+END_SRC
** Which-key
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! which-key-posframe)
#+END_SRC
#+BEGIN_SRC emacs-lisp :tangle config.el
 (use-package! which-key-posframe
   :config
   (setq which-key-posframe-poshandler 'posframe-poshandler-frame-bottom-left-corner
         which-key-posframe-border-width 0)
   (which-key-posframe-mode))
#+END_SRC
* Lang
** Empty
#+begin_src emacs-lisp :tangle config.el
(defun empty-buffer? ()
  (= (buffer-end 1) (buffer-end -1)))
#+end_src
** LSP
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(after! lsp-mode
  (setq lsp-eldoc-render-all nil
        lsp-print-io nil
        lsp-inhibit-message t
        lsp-message-project-root-warning t
        lsp-auto-guess-root t
        lsp-prefer-flymake nil))
#+END_SRC
** Nginx
*** Packages
#+begin_src emacs-lisp :tangle packages.el
(package! nginx-mode)
;;(package! company-nginx)
#+end_src
*** Config
#+begin_src emacs-lisp :tangle config.el
(use-package! nginx-mode
  :mode ("/nginx/sites-\\(?:available\\|enabled\\)/" . nginx-mode))

;;(use-package! company-nginx
;;  :hook (nginx-mode . company-nginx-keywords))
#+end_src
** Org
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! org-tree-slide)
(package! org-super-agenda)
(package! org-sidebar
  :recipe (:host github :repo "alphapapa/org-sidebar"))
(package! secretaria)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(after! org
  (set-popup-rule! "^ \\*Agenda"
    :slot -1 :vslot -1 :size #'+popup-shrink-to-fit :side 'right :ttl 0)

  (setq org-capture-templates
        '(("w" "Work TODO" entry (file+olp "~/org/work.org" "Tasks") "* TODO %? \nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n:PROPERTIES:\n:CATEGORY: TASKS\n:CREATED: %U\n:END:")
          ("o" "Work Overtime" entry (file+olp "~/org/work.org" "COMMENT Overtime") "* %? \nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n:PROPERTIES:\n:CREATED: %U\n:END:")
          ("m" "Work Meetings" entry (file+olp "~/org/work.org" "Meetings") "* %? \nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n:PROPERTIES:\n:CATEGORY: MEETINGS\n:CREATED: %U\n:END:")
          ("t" "Work Training's" entry (file+olp "~/org/work.org" "Training's") "* %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n:PROPERTIES:\n:CATEGORY: TRAINING'S\n:CREATED: %U\n:END:")
          ("S" "Stuff TODO" entry (file+olp "~/org/stuff.org" "Tasks") "* TODO %? \n:PROPERTIES:\n:CATEGORY: TASKS\n:CREATED: %U\n:END:")
          ("M" "Stuff Meetings" entry (file+olp "~/org/stuff.org" "Meetings") "* %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n:PROPERTIES:\n:CATEGORY: MEETINGS\n:CREATED: %U\n:END:")
          ("T" "Stuff Training's" entry (file+olp "~/org/stuff.org" "Training's") "* %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n:PROPERTIES:\n:CATEGORY: TRAINING'S\n:CREATED: %U\n:END:")))

  (setq org-image-actual-width nil))

;; https://github.com/kaushalmodi/.emacs.d/blob/master/setup-files/setup-org.el#L1581
(use-package! org-tree-slide
  :config
  (setq org-tree-slide--lighter " Slide")

  (defvar conf:org-tree-slide-text-scale 4
    "Text scale ratio to default when `org-tree-slide-mode' is enabled.")

  (defun conf:org-tree-slide-set-profile ()
    "Customize org-tree-slide variables."
    (interactive)
    (setq org-tree-slide-header t
          org-tree-slide-slide-in-effect nil
          org-tree-slide-heading-emphasis t
          org-tree-slide-cursor-init t ;Move cursor to the head of buffer
          org-tree-slide-modeline-display 'lighter
          org-tree-slide-skip-done nil
          org-tree-slide-skip-comments t
          org-tree-slide-activate-message (concat "Starting Org presentation. "
                                                  "Use arrow keys to navigate the slides.")
          org-tree-slide-deactivate-message "Ended presentation.")
    (message "Custom `org-tree-slide' profile: ON"))

  (conf:org-tree-slide-set-profile)

  (defvar conf:writegood-mode-state nil
    "Variable to store the state of `writegood-mode'.")

  (defun conf:org-tree-slide-start ()
    "Set up the frame for the slideshow."
    (interactive)
    ;;(internal-show-cursor (selected-window) nil)
    ;;(toggle-read-only)
    (toggle-frame-fullscreen)
    (org-display-inline-images)
    (hide-mode-line-mode 1)
    (text-scale-set conf:org-tree-slide-text-scale))
  (add-hook 'org-tree-slide-play-hook #'conf:org-tree-slide-start)

  (defun conf:org-tree-slide-stop()
    "Undo the frame setup for the slideshow."
    (interactive)
    ;;(internal-show-cursor (selected-window) t)
    ;;(toggle-read-only)
    (toggle-frame-fullscreen)
    (org-remove-inline-images)
    (hide-mode-line-mode -1)
    (text-scale-set 0))
  (add-hook 'org-tree-slide-stop-hook #'conf:org-tree-slide-stop)

  (defun conf:org-tree-slide-text-scale-reset ()
    "Reset time scale to `modi/org-tree-slide-text-scale'."
    (interactive)
    (text-scale-set conf:org-tree-slide-text-scale))

  (defun conf:org-tree-slide-text-scale-inc1 ()
    "Increase text scale by 1."
    (interactive)
    (text-scale-increase 1))

  (defun conf:org-tree-slide-text-scale-dec1 ()
    "Decrease text scale by 1."
    (interactive)
    (text-scale-decrease 1))

  (bind-keys
   :map org-tree-slide-mode-map
   ("C-b" . org-tree-slide-move-previous-tree)
   ("C-f" . org-tree-slide-move-next-tree)
   ("C-0" . conf:org-tree-slide-text-scale-reset)
   ("C-+" . conf:org-tree-slide-text-scale-inc1)
   ("C--" . conf:org-tree-slide-text-scale-dec1)
   ("C-1" . org-tree-slide-content)
   ("C-2" . conf:org-tree-slide-set-profile)
   ("C-3" . org-tree-slide-simple-profile)
   ("C-4" . org-tree-slide-presentation-profile)))

(use-package! org-super-agenda
  :config
  (let ((org-super-agenda-groups))
       '((:log t)  ; Automatically named "Log"
         (:name "Schedule"
                :time-grid t)
         (:name "Today"
                :scheduled today)
         (:habit t)
         (:name "Due today"
                :deadline today)
         (:name "Overdue"
                :deadline past)
         (:name "Due soon"
                :deadline future)
         (:name "Unimportant"
                :todo ("SOMEDAY" "MAYBE" "CHECK" "TO-READ" "TO-WATCH")
                :order 100)
         (:name "Waiting..."
                :todo "WAITING"
                :order 98)
         (:name "Scheduled earlier"
                :scheduled past)))
  (org-agenda-list))

(use-package! org-sidebar
  :config
  (defun conf:org-today-sidebar ()
    "Show my Org Today Sidebar."
    (interactive)
    (org-sidebar
     :sidebars (make-org-sidebar
                :name "Today"
                :description "Today items"
                :items (org-ql (org-agenda-files)
                         (and (not (done))
                              (or (deadline <=)
                                  (scheduled <=)
                                  (date = today)))
                         :action element-with-markers)
                :super-groups '((:time-grid t)
                                (:name "Overdue" :scheduled past :deadline past)
                                (:name "Due today" :scheduled today :deadline today)
                                (:tag "bills")
                                (:priority "A")
                                (:name "Non-tasks"
                                       :todo nil)))))
  (use-package! secretaria
    :config
    (add-hook 'after-init-hook #'secretaria-unknown-time-always-remind-me)))
#+END_SRC
** Python
*** Packages
#+BEGIN_SRC emacs-lisp :tangle no
(package! lsp-python-ms
  :recipe (:host github :repo "andrew-christianson/lsp-python-ms"))
(package! pyenv-mode)
(package! pyenv-mode-auto)
(package! py-isort)
(package! pip-requirements)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle no
(set-pretty-symbols! 'python-mode
  :lambda "lambda"
  :not "not"
  :in "in"
  :not-in "not in"
  :and "and" :or "or")

(use-package! lsp-python-ms
  :demand
  :hook (python-mode . lsp))

(use-package! pyenv-mode
  :hook (python-mode . pyenv-mode))
(use-package! pyenv-mode-auto)

(use-package! py-isort
  :config (setq py-isort-options '("--lines=100"))
  :hook (before-save . py-isort-before-save))

(use-package! pip-requirements
  :hook (pip-requirements-mode . pip-requirements-auto-complete-setup))
#+END_SRC
** PO
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! po-mode
  :mode ("\\.po\\'" . po-mode)
  :config
  ;; Fuente: https://www.emacswiki.org/emacs/PoMode
  (defun po-wrap ()
    "Filter current po-mode buffer through `msgcat' tool to wrap all lines."
    (interactive)
    (if (eq major-mode 'po-mode)
        (let ((tmp-file (make-temp-file "po-wrap."))
              (tmp-buf (generate-new-buffer "*temp*")))
          (unwind-protect
              (progn
                (write-region (point-min) (point-max) tmp-file nil 1)
                (if (zerop
                     (call-process
                      "msgcat" nil tmp-buf t (shell-quote-argument tmp-file)))
                    (let ((saved (point))
                          (inhibit-read-only t))
                      (delete-region (point-min) (point-max))
                      (insert-buffer-substring tmp-buf)
                      (goto-char (min saved (point-max))))
                  (with-current-buffer tmp-buf
                    (error (buffer-string)))))
            (kill-buffer tmp-buf)
            (delete-file tmp-file)))))

  (defun po-guess-language ()
    "Return the language related to this PO file."
    (save-excursion
      (goto-char (point-min))
      (re-search-forward po-any-msgstr-block-regexp)
      (goto-char (match-beginning 0))
      (if (re-search-forward
           "\n\"Language: +\\(.+\\)\\\\n\"$"
           (match-end 0) t)
          (po-match-string 1))))

  (defadvice po-edit-string (around setup-spell-checking (string type expand-tabs) activate)
    "Set up spell checking in subedit buffer."
    (let ((po-language (po-guess-language)))
      ad-do-it
      (if po-language
          (progn
            (ispell-change-dictionary po-language)
            (turn-on-flyspell)
            (flyspell-buffer))))))
#+END_SRC
** Polymode
*** Packges
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! polymode)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! polymode
  :config
  (setq polymode-prefix-key (kbd "C-c n"))
  (define-hostmode poly-python-hostmode :mode 'python-mode)

  (define-innermode poly-sql-expr-python-innermode
    :mode 'sql-mode
    :head-matcher (rx "r" (= 3 (char "\"'")) (* (any space)))
    :tail-matcher (rx (= 3 (char "\"'")))
    :head-mode 'host
    :tail-mode 'host)

  (defun poly-python-sql-eval-chunk (beg end msg)
    "Calls out to `sql-send-region' with the polymode chunk region"
    (sql-send-region beg end))

  (define-polymode poly-python-sql-mode
    :hostmode 'poly-python-hostmode
    :innermodes '(poly-sql-expr-python-innermode)
    (setq polymode-eval-region-function #'poly-python-sql-eval-chunk)
    (define-key poly-python-sql-mode-map (kbd "C-c C-c") 'polymode-eval-chunk))

  ;; Bug? Fix polymode kill chunk so it works.
  (defun polymode-kill-chunk ()
    "Kill current chunk."
    (interactive)
    (pcase (pm-innermost-span)
      (`(,(or `nil `host) ,beg ,end ,_) (delete-region beg end))
      (`(body ,beg ,_ ,_)
       (goto-char beg)
       (pm--kill-span '(body)))
       ;; (pm--kill-span '(head tail))
       ;; (pm--kill-span '(head tail))

      (`(tail ,beg ,end ,_)
       (if (eq beg (point-min))
           (delete-region beg end)
         (goto-char (1- beg))
         (polymode-kill-chunk)))
      (`(head ,_ ,end ,_)
       (goto-char end)
       (polymode-kill-chunk))
      (_ (error "Canoot find chunk to kill"))))

  :hook (python-mode . poly-python-sql-mode))
#+END_SRC
** SQL
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! sqlup-mode)
(package! sql-indent)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! sqlup-mode
  :bind ("C-c u" . sqlup-capitalize-keywords-in-region)
  :init
  (add-hook 'sql-mode-hook 'sqlup-mode)
  (add-hook 'edbi:sql-mode-hook 'sqlup-mode)
  (add-hook 'sql-interactive-mode-hook 'sqlup-mode))

(use-package! sql-indent
  :after sql
  :bind (:map sql-mode-map (("C-c \\" . sql-indent-buffer))))
#+END_SRC
** Xml
#+BEGIN_SRC emacs-lisp :tangle config.el
(defun nxml-template ()
  (interactive)
  (insert "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n"))

(add-hook 'nxml-mode-hook
          '(lambda () (when (empty-buffer?) (nxml-template))))

(use-package! nxml-mode
  :mode (("\\.plist\\'" . nxml-mode)
         ("\\.rss\\'"   . nxml-mode)
         ("\\.svg\\'"   . nxml-mode)
         ("\\.xml\\'"   . nxml-mode)
         ("\\.xsd\\'"   . nxml-mode)
         ("\\.xslt\\'"  . nxml-mode)
         ("\\.pom$"     . nxml-mode))
  :config
  (setq nxml-slash-auto-complete-flag t
        nxml-auto-insert-xml-declaration-flag t)
  (add-to-list 'magic-mode-alist '("<\\?xml" . nxml-mode))
  (mapc
   (lambda (pair)
     (if (or (eq (cdr pair) 'xml-mode)
             (eq (cdr pair) 'sgml-mode))
         (setcdr pair 'nxml-mode)))
   auto-mode-alist)

  ;; https://gist.github.com/DinoChiesa/5489021
  (defun pretty-print-xml-region (begin end)
    "Pretty format XML markup in region. You need to have nxml-mode
      http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
      this. The function inserts linebreaks to separate tags that have
      nothing but whitespace between them. It then indents the markup
      by using nxml's indentation rules."
    (interactive "r")
    (save-excursion
      (nxml-mode)
      ;; split <foo><bar> or </foo><bar>, but not <foo></foo>
      (goto-char begin)
      (while (search-forward-regexp ">[ \t]*<[^/]" end t)
        (backward-char 2) (insert "\n") (incf end))
      ;; split <foo/></foo> and </foo></foo>
      (goto-char begin)
      (while (search-forward-regexp "<.*?/.*?>[ \t]*<" end t)
        (backward-char) (insert "\n") (incf end))
      ;; put xml namespace decls on newline
      (goto-char begin)
      (while (search-forward-regexp "\\(<\\([a-zA-Z][-:A-Za-z0-9]*\\)\\|['\"]\\) \\(xmlns[=:]\\)" end t)
        (goto-char (match-end 0))
        (backward-char 6) (insert "\n") (incf end))
      (indent-region begin end nil)
      (normal-mode))
    (message "All indented!"))

  (defun pretty-print-xml-buffer ()
    "pretty print the XML in a buffer."
    (interactive)
    (pretty-print-xml-region (point-min) (point-max)))

  (define-key nxml-mode-map (kbd "C-x f") 'pretty-print-xml-buffer))
#+END_SRC
* Tools
** Anzu
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! anzu
  :defer t
  :bind (("M-%" . anzu-query-replace)
         ("C-M-%" . anzu-query-replace-regexp))
  :config
  (set-face-attribute 'anzu-mode-line nil :foreground "yellow" :weight 'bold)

  (defun cfg:anzu-update-func (here total)
   (when anzu--state
     (let ((status (cl-case anzu--state)))
       (search (format "[%d/%d Seek]" here total))
       (replace-query (format "(%d Replaces)" total))
       (replace (format "[%d/%d Replaces]" here total))
       (propertize status 'face 'anzu-mode-line))))

  (setq anzu-cons-mode-line-p nil
        anzu-mode-lighter ""
        anzu-deactivate-region t
        anzu-search-threshold 1000
        anzu-replace-threshold 50
        anzu-replace-to-string-separator " => "
        anzu-mode-line-update-function #'cfg:anzu-update-func)

  (add-to-list 'minor-mode-alist
               '(:eval (when anzu--state)
                       (concat " " (anzu--update-mode-line)))))
#+END_SRC
** BugHunter
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! bug-hunter)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! bug-hunter
  :commands (bug-hunter-file bug-hunter-init-file))
#+END_SRC
** Calendar
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! org-caldav)
(package! calfw)
(package! calfw-org)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! org-caldav
  :bind ("<f6>" . org-caldav-sync)
  :config
  (setq org-icalendar-alarm-time 30
        org-icalendar-categories '(all-tags category todo-state)
        org-icalendar-include-todo t
        org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due)
        org-icalendar-use-scheduled '(event-if-todo event-if-not-todo todo-start)
        org-icalendar-with-timestamps t
        org-icalender-sync-todo t
        org-icalendar-timezone "America/Guayaquil")

  (setq org-caldav-calendars '((:calendar-id "arkhan/work"
                                             :files ("~/org/work.org")
                                             :inbox "~/org/inbox.org")
                               (:calendar-id "arkhan/stuff"
                                             :files ("~/org/stuff.org")
                                             :inbox "~/org/inbox.org"))
        org-caldav-files org-agenda-files
        org-caldav-save-directory (concat doom-cache-dir "dav")
        org-caldav-show-sync-results nil
        org-caldav-url "https://cloud.disroot.org/remote.php/dav/calendars")
  (make-directory org-caldav-save-directory :parents)
  (setq org-caldav-backup-file (concat org-caldav-save-directory "caldav-backup.org")))


(use-package! calfw
  :config
  (setq cfw:org-overwrite-default-keybinding t
        cfw:display-calendar-holidays nil
        calendar-week-start-day 1)
  (map! :leader "o f" #'cfw:open-org-calendar))

(use-package! calfw-org)
#+END_SRC
** Docker
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! docker-compose-mode)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! docker-compose-mode
  :mode ("docker-compose.*\.yml\\'" . docker-compose-mode))
#+END_SRC
** COMMENT Format
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! apheleia
  :recipe (:host github :repo "raxod502/apheleia"))
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package!  apheleia
  :init (apheleia-global-mode +1))
#+END_SRC
** Flyspell
#+BEGIN_SRC emacs-lisp :tangle config.el
(add-hook! flyspell
   (setq-default ispell-really-hunspell t
                 ispell-check-comments t
                 ispell-local-dictionary "en_US"
                 ispell-local-dictionary-alist
                 '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)
                   ("es_EC" "[[:alpha:]]" "[^[:alpha:]]" "[ñ]" nil ("-d" "es_EC") nil utf-8))))

(defun switch-dictionary ()
  (interactive)
  (let* ((dic ispell-current-dictionary)
         (change (if (string= dic "en_US") "es_EC" "en_US")))
    (ispell-change-dictionary change)
    (message "Dictionary switched from %s to %s" dic change)))

(defun turn-on-spell-check ()
  (flyspell-mode 1))

(map! "<f7>" #'flyspell-mode!
      (:after flyspell
        :map flyspell-mode-map
        "M-i" #'switch-dictionary
        "C-M-'" #'flyspell-correct-word-generic))
#+END_SRC
** i3wm
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! i3wm-config-mode
  :recipe (:host github :repo "Alexander-Miller/i3wm-Config-Mode"))
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
  (use-package! i3wm-config-mode)
#+END_SRC
** Terminal
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! terminal-here)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! terminal-here
  :bind (("C-<f5>" . terminal-here-launch)
         ("C-<f6>" . terminal-here-project-launch))
  :config (setq terminal-here-terminal-command (list "urxvtcd" "-e" "tmx")))
(after! term
  (custom-set-faces
   '(term ((t (:inherit default :foreground "#ffffff"))))
   '(term-color-black ((t (:background "#000000" :foreground "#31363b"))))
   '(term-color-blue ((t (:background "#2980b9" :foreground "#0099ff"))))
   '(term-color-green ((t (:background "#218058" :foreground "#27ae60"))))
   '(term-color-magenta ((t (:background "#8e44ad" :foreground "#af81ff"))))
   '(term-color-red ((t (:background "#c0392b" :foreground "#f44f4f"))))
   '(term-color-white ((t (:background "#acada1" :foreground "#cfd0c2"))))
   '(term-color-yellow ((t (:background "#fdbc4b" :foreground "#fdbc4b"))))))
#+END_SRC
** COMMENT mu4e
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(when (executable-find "mu")
  (package! mu4e-alert)
  (package! mu4e-maildirs-extension))
(package! link-hint)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! link-hint
  :bind (("C-c l o" . link-hint-open-link)
         ("C-c l c" . link-hint-copy-link)))

(when (executable-find "mu")
  (use-package! mu4e
    :preface
    (defadvice mu4e (before mu4e-start activate)
      "Antes de ejecutar `mu4e' borramos todas las ventanas"
      (when (> 1 (count-windows))
        (window-configuration-to-register :mu4e-fullscreen)
        (delete-other-windows)))

    (defadvice mu4e-quit (after mu4e-close-and-push activate)
      "Despues de salir de mu4e ejecutamos un script para subir los cambios al buzon de correo y para también restaurar la disposición de ventanas"
      (start-process "pushmail" "*pushmail-mbsync*" "mbsync" "-a" "--push")
      (when (get-register :mu4e-fullscreen)
        (jump-to-register :mu4e-fullscreen)))
    :init
    (require 'mu4e-contrib)
    (setq mail-user-agent 'mu4e-user-agent
          message-citation-line-format "\nEl %A %d de %B del %Y a las %H%M horas, %N escribió:\n"
          message-citation-line-function 'message-insert-formatted-citation-line
          message-cite-reply-position 'below
          message-kill-buffer-on-exit t
          message-send-mail-function 'message-send-mail-with-sendmail
          mu4e-attachment-dir  "~/Descargas"
          mu4e-auto-retrieve-keys t
          mu4e-compose-context-policy 'ask
          mu4e-compose-dont-reply-to-self t
          mu4e-compose-keep-self-cc nil
          mu4e-context-policy 'pick-first
          mu4e-headers-date-format "%Y-%m-%d %H:%M"
          mu4e-headers-include-related t
          mu4e-headers-auto-update nil
          mu4e-headers-leave-behavior 'ignore
          mu4e-headers-visible-lines 8
          mu4e-headers-fields '((:date . 25)
                                (:flags . 6)
                                (:from . 22)
                                (:subject . nil))
          mu4e-view-prefer-html t
          mu4e-html2text-command "w3m -dump -T text/html -cols 72 -o display_link_number=true -o auto_image=false -o display_image=true -o ignore_null_img_alt=true"
          mu4e-maildir "~/.mail"
          mu4e-view-show-images t
          sendmail-program "msmtp"
          mu4e-get-mail-command "mbsync -aV")

    (defun mu4e-message-maildir-matches (msg rx)
      (when rx
        (if (listp rx)
            ;; If rx is a list, try each one for a match
            (or (mu4e-message-maildir-matches msg (car rx))
                (mu4e-message-maildir-matches msg (cdr rx)))
          ;; Not a list, check rx
          (string-match rx (mu4e-message-field msg :maildir)))))

    (defun choose-msmtp-account ()
      (if (message-mail-p)
          (save-excursion
            (let*
                ((from (save-restriction
                         (message-narrow-to-headers)
                         (message-fetch-field "from")))
                 (account
                  (cond
                   ((string-match "arkhan@disroot.org" from) "Personal")
                   ((string-match "edison@disroot.org" from) "Work")
                   ((string-match "arkhan.xxx@gmail.com" from) "Gmail"))))
              (setq message-sendmail-extra-arguments (list '"-a" account))))))

    (when (fboundp 'imagemagick-register-types)
      (imagemagick-register-types))

    (add-hook 'mu4e-compose-mode-hook 'flyspell-mode)

    (setq mu4e-contexts
          `( ,(make-mu4e-context
               :name "Personal"
               :enter-func (lambda () (mu4e-message "Switch to the Personal context"))
               :match-func (lambda (msg)
                             (when msg
                               (mu4e-message-maildir-matches msg "^/Personal")))
               :leave-func (lambda () (mu4e-clear-caches))
               :vars '((user-mail-address     . "arkhan@disroot.org")
                       (user-full-name        . "Edison Ibáñez")
                       (mu4e-sent-folder      . "/Personal/Sent")
                       (mu4e-drafts-folder    . "/Personal/Drafts")
                       (mu4e-trash-folder     . "/Personal/Trash")
                       (mu4e-refile-folder    . "/Personal/Archive")))
             ,(make-mu4e-context
               :name "Work"
               :enter-func (lambda () (mu4e-message "Switch to the Work context"))
               :match-func (lambda (msg)
                             (when msg
                               (mu4e-message-maildir-matches msg "^/Work")))
               :leave-func (lambda () (mu4e-clear-caches))
               :vars '((user-mail-address     . "edison@disroot.org")
                       (user-full-name        . "Edison Ibáñez")
                       (mu4e-sent-folder      . "/Work/Sent")
                       (mu4e-drafts-folder    . "/Work/Drafts")
                       (mu4e-trash-folder     . "/Work/Trash")
                       (mu4e-refile-folder    . "/Work/Archive")))
             ,(make-mu4e-context
               :name "Gmail"
               :enter-func (lambda () (mu4e-message "Switch to the Gmail context"))
               :match-func (lambda (msg)
                             (when msg
                               (mu4e-message-maildir-matches msg "^/Gmail")))
               :leave-func (lambda () (mu4e-clear-caches))
               :vars '((user-mail-address     . "arkhan.xxx@gmail.com")
                       (user-full-name        . "Edison Ibáñez")
                       (mu4e-sent-folder      . "/Gmail/Sent Mail")
                       (mu4e-trash-folder     . "/Gmail/Trash")
                       (mu4e-refile-folder    . "/Gmail/All Mail")
                       (mu4e-drafts-folder    . "/Gmail/Drafts")))))

    (add-hook 'message-send-mail-hook 'choose-msmtp-account)
    (run-at-time nil (* 60 5) 'mu4e-update-mail-and-index t)

    (bind-key "C-c c" 'org-mu4e-store-and-capture mu4e-headers-mode-map)
    (bind-key "C-c c" 'org-mu4e-store-and-capture mu4e-view-mode-map))

  (use-package! mu4e-alert
    :init
    (add-hook 'after-init-hook #'mu4e-alert-enable-notifications)
    (add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display)
    (setq mu4e-compose-forward-as-attachment t
          mu4e-compose-crypto-reply-encrypted-policy 'sign-and-encrypt
          mu4e-compose-crypto-reply-plain-policy 'sign
          mu4e-index-update-in-background t
          mu4e-alert-email-notification-types '(subjects))
    :config
    (defun conf:refresh-mu4e-alert-mode-line ()
      (interactive)
      (mu4e~proc-kill)
      (mu4e-alert-enable-mode-line-display))
    (run-with-timer 0 60 'conf:refresh-mu4e-alert-mode-line)
    (mu4e-alert-set-default-style 'libnotify))

  (use-package! mu4e-maildirs-extension
    :after mu4e
    :config (mu4e-maildirs-extension))
  (map! :leader
        (:prefix-map ("M" . "mu4e")
          :desc "Open email app" "M" #'mu4e
          :desc "Compose email"  "c" #'mu4e-compose-new)))
#+END_SRC
** Pass
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! auth-source
  :init (setq auth-source-debug t
              auth-source-do-cache nil))
#+END_SRC
** PKGBUILD
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! pkgbuild-mode)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! pkgbuild-mode
  :mode "PKGBUILD\\'")
#+END_SRC
** Robot
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! robot-mode
  :recipe (:host github :repo "wingyplus/robot-mode"))
#+END_SRC
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! robot-mode)
#+END_SRC
** VLF
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! vlf)
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
(use-package! vlf-setup
  :init (setq vlf-application 'dont-ask))
#+END_SRC
** Tramp
#+begin_src emacs-lisp :tangle config.el
(after! tramp
  (setq tramp-default-method "scp"
        tramp-debug-buffer t
        tramp-verbose 10)
  (tramp-set-completion-function "ssh" '((tramp-parse-sconfig "/etc/ssh_config")
                                         (tramp-parse-sconfig "~/.ssh/config"))))
#+end_src
** xrdb
*** Packages
#+BEGIN_SRC emacs-lisp :tangle packages.el
(package! xrdb-mode
  :recipe (:host github :repo "arkhan/xrdb-mode"))
#+END_SRC
*** Config
#+BEGIN_SRC emacs-lisp :tangle config.el
  (use-package! xrdb-mode
    :mode (("\\.Xdefaults$" . xrdb-mode)
           ("\\Xdefaults$" . xrdb-mode)
           ("\\.Xenvironment$" . xrdb-mode)
           ("\\Xenvironment$" . xrdb-mode)
           ("\\.Xresources$" . xrdb-mode)
           ("\\Xresources$" . xrdb-mode)
           (".*\\.ad$" . xrdb-mode)
           (".*\\.x?rdb$" . xrdb-mode))
    :config
    (add-hook 'xrdb-mode-hook (lambda () (setq comment-start "! "))))
#+END_SRC
